package com.github.limit.fuse.spring.boot.config;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.limit.advise.LimitAdvise;
import com.github.limit.context.LimitContext;
import com.github.limit.fuse.spring.boot.persistence.RedisAccessLimit;
import com.github.limit.handler.*;
import com.github.limit.holder.DefaultLimitAnnotationHolder;
import com.github.limit.holder.DynamicSettingLimitAnnotationHolder;
import com.github.limit.param.AopParam;
import com.github.limit.param.IParam;
import com.github.limit.param.InterceptorParam;
import com.github.limit.persistence.IAccessLimit;
import com.github.limit.response.DefaultResult;
import com.github.limit.response.IResult;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author: wangdejun
 * @create: 2019-10-12 11:16
 **/
@Configuration
public class LimitConfiguration {

    @Bean
    @ConditionalOnMissingBean(RedisTemplate.class)
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    //============  注解处理器bean  =====================

    @Bean
    public MethodAccessLimitHandler accessLimitHandler() {
        return new MethodAccessLimitHandler();
    }

    @Bean
    public TokenAccessLimitHandler tokenAccessLimitHandler() {
        return new TokenAccessLimitHandler();
    }

    @Bean
    public SessionAccessLimitHandler sessionAccessLimitHandler() {
        return new SessionAccessLimitHandler();
    }

    @Bean
    public GlobalAccessLimitHandler globalAccessLimitHandler() {
        return new GlobalAccessLimitHandler();
    }

    @Bean
    public IpAccessLimitHandler ipAccessLimitHandler() {
        return new IpAccessLimitHandler();
    }

    @Bean
    public MethodNameAccessLimitHandler methodNameAccessLimitHandler() {
        return new MethodNameAccessLimitHandler();
    }

    //============  责任链处理bean  =====================
    @Bean
    public LimitContext limitContext() {
        return new LimitContext();
    }

    @Bean
    @ConditionalOnMissingBean(IAccessLimit.class)// 无IAccessLimit时，初始化默认持久化方式
    @ConditionalOnBean(RedisTemplate.class)
    public IAccessLimit redisAccessLimit() {
        return new RedisAccessLimit();
    }

    //============  初始化方法和限流注解映射关系bean  =====================
    @Bean
    @Order(100)
    public DefaultLimitAnnotationHolder defaultLimitAnnotationHolder() {
        return new DefaultLimitAnnotationHolder();
    }

    @Bean
    @Order(200)
    public DynamicSettingLimitAnnotationHolder dynamicSettingLimitAnnotationHolder() {
        return new DynamicSettingLimitAnnotationHolder();
    }


    @Bean
    @ConditionalOnClass(JSONObject.class)
    @ConditionalOnMissingBean(IResult.class)
    public IResult defaultResult() {
        return new DefaultResult();
    }

    @Bean
    @ConditionalOnMissingBean(WebMvcConfig.class)
    public LimitAdvise limitAdvice() {
        return new LimitAdvise();
    }

    @Bean
    @ConditionalOnMissingBean(IParam.class)
    public InterceptorParam interceptorParam() {
        return new InterceptorParam();
    }

}
